
using System;
using System.Collections.Generic;
using System.IO;
using System.Xml.Serialization;

using Derm;
using Derm.Render;

namespace ShaderGen
{
	public class Validation
	{
		[XmlElement("ProjectPath")]
		public string ProjectPath;

		[XmlElement("ProjectOutput")]
		public string ProjectOutput;

		[XmlElement("Test")]
		public List<ValidationTest> Tests;

		public bool BuildShaderLibrary()
		{
			return (true);
		}
	}

	public class ValidationTest
	{
		[XmlAttribute("Program")]
		public string Program;

		[XmlElement("CompilerContext")]
		public List<ShaderCompilerContext> CompilerContexts;

		public void ExecuteTest(RenderContext ctx)
		{
			ShaderCacheService cacheService = ShaderCacheService.GetService(ctx);

			Console.WriteLine("Testing shader program {0}:", Program);

			if (CompilerContexts.Count == 0)
				CompilerContexts.Add(new ShaderCompilerContext());

			foreach (ShaderCompilerContext cctx in CompilerContexts) {
				Console.WriteLine("  -----------");
				Console.WriteLine("  *** Shader compiler context ***");
				Console.Write("  - GLSL version {0}", (int)cctx.ShaderVersion);
				if (cctx.Defines.Count > 0) {
					Console.Write(", preprocessor symbols: ");
					for (int i = 0; i < cctx.Defines.Count; i++) {
						if (i + 1 < cctx.Defines.Count)
							Console.Write("{0},", cctx.Defines[i]);
						else
							Console.Write("{0}", cctx.Defines[i]);
					}
				}
				Console.WriteLine();

				try {
					ShaderProgram shaderProgram = cacheService.CreateShaderProgram(ctx, cctx, Program);

					Console.WriteLine("  *** Shader program information ***");
					Console.WriteLine("  - Active inputs ({0})", shaderProgram.ActiveAttributes.Count);
					foreach (string activeAttribute in shaderProgram.ActiveAttributes)
						Console.WriteLine("    - {0}", activeAttribute);
					Console.WriteLine("  - Active uniforms ({0}, {1} slots)", shaderProgram.ActiveUniforms.Count, shaderProgram.UniformSlots);
					foreach (string activeUniform in shaderProgram.ActiveUniforms)
						Console.WriteLine("    - {0}", activeUniform);
					
				} catch (Exception e) {
					Exception exceptionIter = e.InnerException;

					Console.WriteLine("Shader program creation failed: {0}", e.Message);
					while (exceptionIter != null) {
						Console.WriteLine("  {0}", exceptionIter.Message);
						exceptionIter = exceptionIter.InnerException;
					}
				}
				Console.WriteLine();
			}
			Console.WriteLine();
		}
	}

	public class ValidationCompilerContext
	{
		[XmlElement(ElementName = "PreprocessorSymbol")]
		public List<string> PreprocessorSymbols;
	}

	static class ShaderValidator
	{
		public static void ValidateLibrary(Validation config)
		{
			using (RenderWindow window = new RenderWindow(0, 0)) {
				RenderSurfaceFormat windowFormat = new RenderSurfaceFormat(Pixel.Type.RGB24);
				window.Create(windowFormat);

				using (RenderContext ctx = new RenderContext(window.GetDeviceContext())) {
					ctx.MakeCurrent(true);

					Console.WriteLine("Loading shader library from {0}", Path.GetFullPath(config.ProjectOutput));
					Derm.Render.ShaderLibrary.LoadShaders(config.ProjectOutput);
					Console.WriteLine("  - Shader include tree:");
					foreach (string s in Derm.Render.ShaderLibrary.GetShaderLibraryIncludes())
						Console.WriteLine("    > {0}", s);
					Console.WriteLine("  - Available shader objects:");
					foreach (string s in Derm.Render.ShaderLibrary.GetShaderLibraryObjects())
						Console.WriteLine("    > {0}", s);
					Console.WriteLine("  - Available shader programs:");
					foreach (string s in Derm.Render.ShaderLibrary.GetShaderLibraryPrograms())
						Console.WriteLine("    > {0}", s);

					Console.WriteLine("Validate shaders found in library");
					Console.WriteLine();

					foreach (ValidationTest test in config.Tests)
						test.ExecuteTest(ctx);
				}
			}
		}
	}
}
